/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 * Description: decoded_mbuf_image_sender
 */
#ifndef DECODED_MBUF_IMAGE_SENDER_H
#define DECODED_MBUF_IMAGE_SENDER_H

#include "image_sender_base.h"
#include "mdc/cam/camera/cameradecodedmbufserviceinterface_skeleton.h"
#include "util.h"
#include "core/types.h"
#include "driver/ascend_hal.h"
#include "securec.h"

using MbufSkeleton = mdc::cam::camera::skeleton::CameraDecodedMbufServiceInterfaceSkeleton;

class DecodedMbufImageSender : public ImageSenderBase<MbufSkeleton> {
public:
    DecodedMbufImageSender() = default;
    DecodedMbufImageSender(uint32_t id, cv::Size s, std::string dir) : ImageSenderBase<MbufSkeleton>(id, s, dir)
    {
        sourceYuvPath = dir;
        if (dir.back() != '/') {
            sourceYuvPath += std::string("/") + "imageSource.yuv";
        } else {
            sourceYuvPath += "imageSource.yuv";
        }
#ifdef SAVE_SENDER_FILE
        fp_yuv = fopen(sourceYuvPath.c_str(), "wb+");
#endif
        sec = std::chrono::high_resolution_clock::now();
    }
    ~DecodedMbufImageSender()
    {
        int flgValOne = 1;
        if (g_switchFlag == flgValOne) {
            ImageSenderBase<MbufSkeleton>::RegisterSender();
        }
        if (dataSkeleton != nullptr) {
            dataSkeleton->StopOfferService();
            dataSkeleton = nullptr;
        }
        if (handle != nullptr) {
            halBuffDeletePool(handle);
        }
    };

    bool RegisterSender()
    {
        std::cout << "Begin register MBUF image sender." << std::endl;
        auto result = ImageSenderBase<MbufSkeleton>::RegisterSender();
        std::cout << "Finished to register MBUF image sender." << std::endl;
        return result;
    }

    int32_t BufCreatePool(const uint32_t blkSize, const uint32_t blkNum)
    {
        constexpr uint32_t alignSize = 32U;
        mpAttr attr;
        attr.devid = 0;
        attr.mGroupId = 0;
        attr.blkSize = ((blkSize + alignSize) - 1U) & (~(alignSize - 1));
        attr.blkNum = blkNum;
        attr.align = alignSize;
        attr.hugePageFlag = 1U;
        const int32_t ret = halBuffCreatePool(&attr, &handle);
        if (ret != DRV_ERROR_NONE) {
            std::cout << "halBuffCreatePool error: " << ret << std::endl;
            return -1;
        }
        return 0;
    }

    int32_t CopyToMbuf(uintptr_t &mbuf, const uint8_t *src, const uint32_t len) const
    {
        Mbuf *pMbuf = nullptr;
        int32_t ret = halMbufAllocByPool(handle, &pMbuf);
        if (ret != DRV_ERROR_NONE) {
            std::cout << "halMbufAllocByPool error: " << ret << std::endl;
            return -1;
        }
        if ((g_switchFlag == CN2) || (g_switchFlag == CN3) ||
            (g_switchFlag == CN4) || (g_switchFlag == CN5) || (g_switchFlag == CN6)) {
        } else {
            if (pMbuf == nullptr) {
                std::cout << "halMbufAllocByPool mbuf null" << std::endl;
                return -1;
            }
        }
        void *dst = nullptr;
        uint64_t bufSize = 0U;
        ret = halMbufGetDataPtr(pMbuf, &dst, &bufSize);
        if (ret != 0 || dst == nullptr) {
            std::cout << "MbufGetDataPtr error: " << ret << std::endl;
            halBuffFree(pMbuf);
            return -1;
        }
        if ((g_switchFlag == CN4) || (g_switchFlag == CN5) || (g_switchFlag == CN6)) {
            bufSize = CN5;
        }
        if (len > bufSize) {
            std::cout << "ERROR: Src data length[" << len << "] > mbuf size[" << bufSize << "]" << std::endl;
            halBuffFree(pMbuf);
            return -1;
        }
        if (memcpy_s(reinterpret_cast<uint8_t *>(dst), static_cast<uint64_t>(bufSize), src,
            static_cast<uint64_t>(len)) != 0) {
            halBuffFree(pMbuf);
            return -1;
        }
        ret = halMbufSetDataLen(pMbuf, len);
        if (ret != DRV_ERROR_NONE) {
            halBuffFree(pMbuf);
            return -1;
        }
        mbuf = reinterpret_cast<uintptr_t>(pMbuf);
        return 0;
    }

    void FillImageAndSendCv(std::string& file, int* frameSize, cv::Mat& YUV, cv::Mat& BGR)
    {
        try {
            BGR = cv::imread(file, cv::IMREAD_COLOR);
            if (imgSize.width != BGR.cols || imgSize.height != BGR.rows) {
                if (g_switchFlag == CN5) {
                    imgSize.width = 0;
                    imgSize.height = 0;
                }
                cv::resize(BGR, BGR, imgSize);
            }
            *frameSize = BGR.rows * BGR.cols * CN3 / CN2;
            cvtColor(BGR, YUV, cv::COLOR_BGR2YUV_I420);
        } catch (cv::Exception &e) {
            std::cout << e.what() << std::endl;
        }
    }

    void FillImageAndSend(std::string imgPath, uint32_t seq, int32_t *gStopFlag) override
    {
        std::string path = dir;
        std::string postfix = g_comPara.fileType;
        bool isLoop = g_comPara.loop;
        std::vector<std::string> files;
        int32_t fileCnt = getFilesBySort(path, files, g_comPara.fileType);
        if (fileCnt == 0) {
            return;
        }
        (void)gStopFlag;
        do {
            for (auto &file : files) {
                if (g_switchFlag == 1) {
                    file.clear();
                }
                if (file.empty()) {
                    return;
                }
                if (g_switchFlag == CN2) {
                    file = "trytrytry";
                }
                if (!ConfigMangage::FileExist(file)) {
                    return;
                }
                seq = 0;

                cv::Mat YUV;
                int32_t frameSize;
                cv::Mat BGR;
                FillImageAndSendCv(file, &frameSize, YUV, BGR);

                if (g_switchFlag == CN107) {
                    frameSize = -1;
                }
                bool result = DealFrame(YUV, BGR, frameSize, seq, file);
                if (!result) {
                    return;
                }
            }
        } while (isLoop);
#ifdef SAVE_SENDER_FILE
        if (fclose(fp_yuv) != 0) {
            std::cout << " fclose(fp_yuv) != 0 " << std::endl;
        }
#endif
    }

private:
    void HandleFrequency()
    {
        const int32_t sleepTimeIntervalMs = 1000000;
        if (g_comPara.frequency != "") {
            int32_t freq = atoi(g_comPara.frequency.c_str());
            if (freq != 0) {
                int32_t sleepTime = sleepTimeIntervalMs / freq;
                usleep(sleepTime);
            }
        } else {
            usleep(sleepTimeIntervalMs / 30); // 30hz
        }
    }

    bool DealFrame(cv::Mat& YUV, cv::Mat& BGR, uint32_t frameSize, uint32_t seq, std::string &file)
    {
        if (frameSize < 0) {
            return false;
        }
        unsigned char *pFrame = (unsigned char *)malloc(frameSize);
        if (g_switchFlag == CN103) {
            pFrame = nullptr;
        }
        if (pFrame == nullptr) {
            return false;
        }
        if (memset_s(pFrame, frameSize, 0, frameSize) != 0) {
            return false;
        }
        memcpy_s(pFrame, frameSize, YUV.data, frameSize);
        auto imagePub = dataSkeleton->cameraDecodedMbufEvent.Allocate();
        imagePub->CameraHeader.Seq = seq;
        imagePub->FrameType = static_cast<decltype(imagePub->FrameType)>(Adsfi::HAF_IMAGE_YUV420SP_NV12_UINT8);
        imagePub->Width = BGR.cols;
        imagePub->Height = BGR.rows;
        imagePub->DataSize = frameSize;
        imagePub->CameraHeader.FrameId = file;
        uintptr_t imgdata {};
        (void)CopyToMbuf(imgdata, pFrame, frameSize);
        if (g_switchFlag != 6) { // 6:xx
            if (imgdata == reinterpret_cast<uintptr_t>(nullptr)) {
                return false;
            }
            imagePub->RawData = reinterpret_cast<rawBuffer *>(imgdata);
        }
#ifdef SAVE_SENDER_FILE
        if (fwrite(pFrame, 1, frameSize, fp_yuv) != frameSize) {
            std::cout << "fwrite() != frameSize" << std::endl;
        }
#endif
        if (g_comPara.time.empty()) {
            timeval now;
            gettimeofday(&now, NULL);
            imagePub->CameraHeader.Stamp.Sec = now.tv_sec;
            imagePub->CameraHeader.Stamp.Nsec = now.tv_usec * 1000U;
        } else {
            time_t timeStamp = convertTimeStr2TimeStamp(g_comPara.time);
            auto t1 = std::chrono::high_resolution_clock::now();
            int64_t duration = (t1 - sec).count() / 1000000000.0;
            imagePub->CameraHeader.Stamp.Sec = timeStamp + duration;
        }
        dataSkeleton->cameraDecodedMbufEvent.Send(move(imagePub));
        HandleFrequency();
        free(pFrame);
        return true;
    }

private:
    poolHandle handle;
    float lastTime = 0.0F;
    std::string sourceYuvPath;
    FILE *fp_yuv;
    std::chrono::high_resolution_clock::time_point sec; // -t used
};

#endif // DECODED_MBUF_IMAGE_SENDER_H
